#pragma once

#include <Windows.h>
#include <string>
#include <regex>
#include <format>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <filesystem>
#include "duckx.hpp"
#include "xlnt/xlnt.hpp"
#include "nlohmann/json.hpp"



inline std::string begin_prefix_str;
inline std::string end_prefix_str;
inline std::string safety_key_str;
inline std::vector<std::string> sil_0_vector;
inline std::vector<std::string> sil_2_vector;
inline std::vector<std::string> sil_4_vector;
inline std::string type_key_str;
inline std::string source_key_str;





namespace FileReader {

/**
 * @brief 需求描述的键名，用于在映射中存储需求描述信息
 */
#define DESCRIPTION_KEY ("Description")
/**
 * @brief 安全等级的键名，用于在映射中存储安全等级信息
 */
#define SAFETY_KEY ("Safety")
/**
 * @brief 需求类型的键名，用于在映射中存储需求类型信息
 */
#define TYPE_KEY ("Type")





/**
 * @brief DOCX文件读取器类，用于解析DOCX格式的需求文档并提取需求信息
 */
class DocxFileReader {
public:
    /**
     * @brief 构造函数，初始化DOCX文件路径
     * @param filePath DOCX文件的路径
     */
    DocxFileReader(const std::string& filePath);
    /**
     * @brief 析构函数，释放资源
     */
    ~DocxFileReader();
    /**
     * @brief 打开DOCX文件并准备读取
     * @return 成功打开返回true，否则返回false
     */
    bool openFile();
    std::string getContentByString(const std::string& context); // Method to get context by string (not implemented in this snippet).
    std::unordered_map<std::string, std::unordered_map<std::string, std::string>> getAllRequirements();
    std::unordered_map<std::string, std::vector<std::string>> getRetroRelationship();

    template <typename T>
    static void concatenateMaps(std::unordered_map<std::string, T>& map1, const std::unordered_map<std::string, T>& map2);

    // inline functions
    inline std::string readNextParagraph() {
        // This method is not implemented in this snippet.
        // It should read the next paragraph from the DOCX file.
        // For now, we return an empty string.
        std::string res = "";
        if (m_paragraph.has_next()) {
            for (const auto& run : std::as_const(m_paragraph.runs()) ) {
                if (!run.get_text().empty()) {
                    // If the run has text, we can return it.
                    res += run.get_text();
                }
            }
            m_paragraph = m_paragraph.next();
        }
        
        return res;
    }
    inline bool hasNextParagraph(){
        // This method checks if there is a next paragraph to read.
        // It returns true if there is a next paragraph, false otherwise.
        return m_paragraph.has_next();
    }

    inline std::string getNextRequirement() {
        // This method is not implemented in this snippet.
        // It should return the next requirement from the DOCX file.
        // For now, we return an empty string.
        std::string res = "";
        std::string regex_str = std::format(R"(^\[(?!{}|{})([^\]]+)\]$)", begin_prefix_str, end_prefix_str);
        std::regex pattern(regex_str);
        bool end_flag = false;
        while(hasNextParagraph()){
            std::string paragraph = readNextParagraph();
            std::smatch match;
            if(!regex_search(paragraph, match, pattern)) continue;

            if(match.size() <= 1) continue;
                
            res += paragraph;
            std::string sub_str = match[1].str(); // Return the paragraph if it matches the pattern.
            std::string end_pattern_str = std::format(R"(^\[{}{}\]$)", end_prefix_str, sub_str);
            std::regex end_pattern(end_pattern_str);
            while(hasNextParagraph()) {
                std::string t_nextParagraph = readNextParagraph();
                if (t_nextParagraph.empty()) {
                    // If the next paragraph is empty, we continue to the next iteration.
                    continue;
                }

                if(regex_match(t_nextParagraph, end_pattern)) {
                    // If the end pattern matches, we return the sub string.
                    end_flag = true;
                    res += std::format("\n[{}{}]", end_prefix_str, sub_str);
                    break;
                } else {
                    res += "\n" + t_nextParagraph;
                }

            }

            if (end_flag) {
                break;
            }
        }

        return res;
    }


    inline std::string getContextStringByRegexStr(const std::string& context, const std::string& regexStr) const {
        // This method returns the context string from the context map.
        // If the context is not found, it returns an empty string.
        std::string res = "";

        std::regex pattern(regexStr);

        std::smatch match;
        if (regex_search(context, match, pattern)) {
            if (match.size() > 1) {
                res = match[1].str(); // Return the context string if it matches the pattern.
            }
        }
        return res;
    }

    inline void readAll(){
        // This method is not implemented in this snippet.
        // It should read all requirements from the DOCX file.
        // For now, we do nothing.
        std::string content;

        while (!(content = getNextRequirement()).empty()) {
            std::unordered_map<std::string, std::string> contextMap;
            std::vector<std::string> retroVector;

            std::string mainKey = getContextStringByRegexStr(content, std::format(R"(^\[(?!{}|{})([^\]]+)\]$)", begin_prefix_str, end_prefix_str));
            if (mainKey.empty()) {
                // If the main key is empty, we continue to the next iteration.
                continue;
            }
            // cout << "content:\n" << content << endl << endl;
            // if (mainKey.find("OBCU_SuRS_6365") != std::string::npos)
            // cout <<"ID:" <<mainKey;
            std::string description_pattern_str = std::format(R"(\[{}\]([\S\s]*)\[{}{}\])", mainKey, begin_prefix_str, mainKey);
            contextMap[DESCRIPTION_KEY] = getContextStringByRegexStr(content, description_pattern_str);
            // cout <<"\tDES:" <<contextMap[DESCRIPTION_KEY];
            std::string safety_pattern_str = std::format(R"(.*(?:{})\s*:(.*))", safety_key_str);
            contextMap[SAFETY_KEY] = getContextStringByRegexStr(content, safety_pattern_str);
            // cout <<"\tSAFETY:" <<contextMap[SAFETY_KEY];
            std::string type_pattern_str = std::format(R"(.*(?:{})\s*:(.*))", type_key_str);
            contextMap[TYPE_KEY] = getContextStringByRegexStr(content, type_pattern_str);
            // cout <<"\tTYPE:" <<contextMap[TYPE_KEY] << endl;
            
            m_requirements[mainKey] = contextMap;
            std::string source_pattern_str = std::format(R"(.*(?:{})\s*:\s*([\s\S]*)[\s\S]*\[{}{}\])", source_key_str, end_prefix_str, mainKey);
            std::string sources_text = getContextStringByRegexStr(content, source_pattern_str);
            if (!sources_text.empty()) {
                // Split the sources_text by comma and trim whitespace.
                std::regex source_regex(R"(\s*(.*)\s*)");

                std::sregex_token_iterator iter(sources_text.begin(), sources_text.end(), source_regex, 1);
                std::sregex_token_iterator end;
                for (; iter != end; ++iter) {
                    std::string source = iter->str();
                    if (!source.empty()) {
                        if (source.find(":") != std::string::npos) continue;
                        retroVector.push_back(source); // Add each source to the retroVector.
                    }
                }
            }
            
            m_retroRelationship[mainKey] = retroVector;
            
        }
    }


    

private:
    std::string m_filePath; // Private member to store the file path.
    duckx::Document m_docx; // Document object from the duckx library to handle DOCX files.
    duckx::Paragraph m_paragraph; // Paragraph object to handle paragraphs in the DOCX file.
    std::unordered_map<std::string, std::unordered_map<std::string, std::string>> m_requirements; // Hash map to store requirements, mapping main keys to context maps.
    std::unordered_map<std::string, std::vector<std::string>> m_retroRelationship; // Hash map to store retro relationships, mapping context strings to lists of strings.
};



class ExcelFileReader {
public:
    inline static std::unordered_map<std::string, std::unordered_map<std::string, std::string>> read(const std::string& file){

        std::unordered_map<std::string, std::unordered_map<std::string, std::string>> requirements;

        xlnt::workbook wb; // Create a workbook object from the xlnt library to handle Excel files.
        std::filesystem::path filePath(file);
        wb.load(filePath.wstring()); // Load the Excel file using xlnt library.
        xlnt::worksheet ws = wb.active_sheet();
       
        for(auto const& row : ws.rows(false)) {
            std::string mainKey;
            std::unordered_map<std::string, std::string> contextMap;
            contextMap[DESCRIPTION_KEY] = "";
            contextMap[SAFETY_KEY] = "";
            contextMap[TYPE_KEY] = "";
            for (auto const cell : row) {
                if (cell.has_value()) {
                    std::string cellValue = cell.to_string();
                    if (cell.column() == 1) {
                        // Assuming the first column contains the main key.
                        mainKey = cellValue;
                    } else if (cell.column() == 2) {
                        // Assuming the second column contains the description.
                        contextMap[DESCRIPTION_KEY] = cellValue;
                    } else if (cell.column() == 3) {
                        // Assuming the third column contains the safety information.
                        contextMap[SAFETY_KEY] = cellValue;
                    } else if (cell.column() == 4) {
                        // Assuming the fourth column contains the type.
                        contextMap[TYPE_KEY] = cellValue;
                    }
                }
            }
            if (!mainKey.empty()) {
                requirements[mainKey] = contextMap; // Add the context map to the requirements under the main key.
            }
        }
        
        return requirements;
    }
};


} // namespace FileReader













